package com.distribute.framework.core.util;

import com.distribute.framework.core.lang.bit;
import com.distribute.framework.core.lang.expr;
import com.distribute.framework.core.lang.flag;
import com.distribute.framework.core.lang.size;
import com.distribute.framework.core.web.helper.Utils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

public class CStruct implements java.io.Serializable{
	
	private transient Map<String,byte[]> decodeByteMapping = new HashMap();
	private transient Map<String,byte[]> encodeByteMapping = new HashMap();
	private byte[] decodeBytes = null;
	private byte[] encodeBytes = null;
	private byte[] unusedBytes = null;

	public static boolean reverse	= true;	// 是否字节逆序

	/**
	 * 获取Object字段的最终类型
	 * @param name
	 * @return
	 */
	public Class getClassType(String name){
		return null;
	}
	
	public byte[] toBytes() throws Exception {
		return CStruct.encode(this);
	}
	
	public Map<String,String> toDecodeHexMapping() throws Exception{
		if(this.decodeByteMapping==null)this.decodeByteMapping=new HashMap();
		Map<String, String> mapping = getHex(this.decodeByteMapping);
		if(this.decodeBytes!=null){
			mapping.put("byte[]", CStruct.toHex(this.decodeBytes));
		}
		return mapping;
	}
	public Map<String,String> toEncodeHexMapping() throws Exception{
		if(this.encodeBytes==null)this.encode(this);
		if(this.encodeByteMapping==null)this.encodeByteMapping=new HashMap();
		Map<String, String> mapping = getHex(this.encodeByteMapping);
		mapping.put("byte[]", CStruct.toHex(this.encodeBytes));
		return mapping;
	}
	public Map<String,String> getHex(Map<String,byte[]> bytemapping) throws Exception {
		Map<String,String> map = new LinkedHashMap();
		Field[] fields = this.getClass().getDeclaredFields();
		for (Field field : fields) {
			byte[] bytes = bytemapping.get(field.getName());
			if(bytes!=null){
				map.put(field.getName(), CStruct.toHex(bytes));
			}
		}
		return map;
	}
	
	// 转java对象到数组
	public static byte[] encode(CStruct entity) throws Exception {
		ByteArrayOutputStream baOs = new ByteArrayOutputStream();
		write(new DataOutputStream(baOs), entity);
		entity.encodeBytes = baOs.toByteArray();
		return entity.encodeBytes;
	}

	/**
	 * 写数据到输出流
	 * 
	 * @param dos
	 * @param entity
	 * @throws Exception
	 */
	public static void write(DataOutputStream dos, CStruct entity) throws Exception {
		if(entity==null)return;
		if(entity.encodeByteMapping==null)entity.encodeByteMapping=new HashMap();
		Field[] fields = entity.getClass().getDeclaredFields();
		EvaluationContext context = new StandardEvaluationContext();
		for (Field field : Objects.requireNonNull(ReflectUtils.removeStaticField(fields))) {
			field.setAccessible(true);
			
			String name = field.getName();
			Class<?> type = field.getType();
			String typeName = type.getSimpleName();
			if(typeName.equals("CStruct")){
				if(entity.getClassType(name)!=null){
					type = entity.getClassType(name);
					typeName = type.getSimpleName();
				}else if(field.get(entity)!=null){
					type = field.get(entity).getClass();
					typeName = type.getSimpleName();
				}
			}
			
			size size = field.getAnnotation(size.class);
			bit bit = field.getAnnotation(bit.class);
			expr expr = field.getAnnotation(expr.class);
			flag flag = field.getAnnotation(flag.class);
			if(size==null && bit==null && expr==null){
				throw new RuntimeException(entity.getClass().getSimpleName()+"字段"+field.getName()+"未标记@size|@bit|@expr");
			}
			
			int bited = 0;//已写位数
			int length = 0;//定义的长度
			if(size!=null){
				length = size.value();
				bited+=size.value()*8;
			}else if(bit!=null){
				//按位写方式未实现处理
				throw new RuntimeException("按位写方式未实现处理");
			}else if(expr!=null){
				ExpressionParser parser = new SpelExpressionParser();
				Expression exp = parser.parseExpression(expr.value());
				Integer len = Utils.parseInt(exp.getValue(context));				
				length = len;
				bited+=len*8;
			}
			context.setVariable(name, field.get(entity));
			
			byte[] bytes = new byte[length];				
			
			if (typeName.equals("int") || typeName.equals("Integer")){
				dos.write(bytes=convert(length,field.getInt(entity)));
			}else if (typeName.equals("long") || typeName.equals("Long")){
				dos.write(bytes=convert(length,field.getLong(entity)));
			}else if (typeName.equals("short") || typeName.equals("Short")){
				dos.write(bytes=convert(length,field.getShort(entity)));
			}else if (typeName.equals("byte") || typeName.equals("Byte")){
				dos.write(bytes=convert(length,field.getByte(entity)));
			}else if (typeName.equals("String")) {
				String str = (String) field.get(entity);
				if (str == null)
					str = "";
				str = str.replaceAll("　", " ");
				
				byte[] bts = null;
				if(flag!=null && flag.value().equals("ASCII")){
					bts = CStruct.StrToAsc(str);
				}else{
					bts = str.getBytes();
				}
				
				if(length==0){//如果长度未设置，以当前实际的字节为准
					bytes = new byte[bts.length];
				}
				System.arraycopy(bts, 0, bytes, 0, bts.length);
				dos.write(bytes);
			} else if (typeName.equals("byte[]")) {
				byte[] bts = (byte[]) field.get(entity);
				if(length==0){//如果长度未设置，以当前实际的字节为准
					bytes = new byte[bts.length];
				}
				System.arraycopy(bts, 0, bytes, 0, bts.length);
				dos.write(bytes);
			}else if (type.isArray()) {
				ByteArrayOutputStream baOs1 = new ByteArrayOutputStream();
				DataOutputStream dos1 = new DataOutputStream(baOs1);
				for (Object object : (Object[]) field.get(entity)){
					write(dos1, (CStruct) object);
				}
				byte[] bts  = baOs1.toByteArray();
				if(length==0){//如果长度未设置，以当前实际的字节为准
					bytes = new byte[bts.length];
				}
				System.arraycopy(bts, 0, bytes, 0, bts.length);
				dos.write(bytes);
			} else {
				byte[] bts = encode((CStruct) field.get(entity));
				if(length==0){//如果长度未设置，以当前实际的字节为准
					bytes = new byte[bts.length];
				}
				System.arraycopy(bts, 0, bytes, 0, bts.length);
				dos.write(bytes);
			}
			entity.encodeByteMapping.put(name, bytes);
		}
	}

	
	public static byte[] convert(int size, long value) throws Exception{
		ByteArrayOutputStream baOs = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(baOs);
		switch(size){
		case 1://byte
			dos.write((byte) value);
			break;
		case 2://short
			dos.writeShort(reverse ? Short.reverseBytes((short)value) : (short)value);
			break;
		case 4://int
			dos.writeInt(reverse ? Integer.reverseBytes((int)value) : (int)value);
			break;
		case 8://long
			dos.writeLong(reverse ? Long.reverseBytes((long)value) : (long)value);
			break;
		}
		return baOs.toByteArray();
	}
	
	public static long convertEx(long value,int size) throws Exception{
		switch(size){
		case 1://byte
			return value;
		case 2://short
			return (reverse ? Short.reverseBytes((short)value) : (short)value);
		case 4://int
			return (reverse ? Integer.reverseBytes((int)value) : (int)value);
		case 8://long
			return (reverse ? Long.reverseBytes((long)value) : (long)value);
		}
		return 0;
	}
	

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T extends CStruct> T decode(Class<? extends CStruct> cls,byte[] bytes) throws Exception{
		Field[] fields = cls.getDeclaredFields();
		CStruct instance = cls.newInstance();
		if(instance.decodeByteMapping==null)instance.decodeByteMapping=new HashMap();
		EvaluationContext context = new StandardEvaluationContext();
		int bited = 0;//已读位数
		for(Field field: Objects.requireNonNull(ReflectUtils.removeStaticField(fields))){
			size size = field.getAnnotation(size.class);
			bit bit = field.getAnnotation(bit.class);
			expr expr = field.getAnnotation(expr.class);
			flag flag = field.getAnnotation(flag.class);
			if(size==null && bit==null && expr==null){
				throw new RuntimeException(cls.getSimpleName()+"字段"+field.getName()+"未标记@size|@bit|@expr");
			}
			String name = field.getName();
			Class<?> type = field.getType();
			String typeName = type.getSimpleName();
			if(typeName.equals("CStruct")){
				if(instance.getClassType(name)!=null){
					type = instance.getClassType(name);
					typeName = type.getSimpleName();
				}
			}
			byte[] bts = null;
			if(size!=null){
				bts = new byte[size.value()];
				System.arraycopy(bytes, bited/8, bts, 0, size.value());
				bited+=size.value()*8;
			}else if(bit!=null){
				int val = 0;
				String strBin = "";
				for(int i=bited;i<bited+bit.value();i++){//逐位扫描
					if((bytes[i/8] & (1 << (i%8))) != 0){//如果位为1
						strBin+="1";
					}else{
						strBin+="0";
					}
					val = Integer.parseInt(strBin, 2);
				}				
				byte[] result = new byte[4];
				result[3] = (byte)(val);
				result[2] = (byte)(val >> 8);
				result[1] = (byte)(val >> 16);
				result[0] = (byte)(val >> 24);
				bts = new byte[(int)Math.ceil((double)bit.value()/8.0)];
				System.arraycopy(result, result.length-bts.length, bts, 0, bts.length);
				bited+=bit.value();
			}else if(expr!=null){
				ExpressionParser parser = new SpelExpressionParser();
				Expression exp = parser.parseExpression(expr.value());
				Integer len = Utils.parseInt(exp.getValue(context));				
				bts = new byte[len];
				System.arraycopy(bytes, bited/8, bts, 0, len);
				bited+=len*8;
			}
			if(bts.length==0)continue;
			DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bts));
			if (typeName.equals("int") || typeName.equals("Integer")){
				field.setInt(instance,(int) convertEx(dis.readInt(),4));
			}else if (typeName.equals("long") || typeName.equals("Long")){
				field.setLong(instance, convertEx(dis.readLong(),8));
			}else if (typeName.equals("short") || typeName.equals("Short")){
				field.setShort(instance, (short) convertEx(dis.readShort(),2));
			}else if (typeName.equals("byte") || typeName.equals("Byte")){
				field.setByte(instance, (byte) convertEx(dis.readByte(),1));
			}else if (typeName.equals("byte[]")){
				field.set(instance, bts);
			}else if (typeName.equals("String")) {
				String strStr = new String(bts,"UTF-8");
				if(flag!=null && flag.value().equals("BCD")){
					strStr = bcd2Str(bts);
				}else if(flag!=null && flag.value().equals("ASCII")){
					strStr = CStruct.AscToStr(bts);
				}
				strStr = strStr.replaceAll("[\u0000]", "");
				field.set(instance, strStr);
			}else if (type.isArray()) {
				byte[] arrayBytes = bts;
				List list = new ArrayList();
				while(arrayBytes.length>0 && list.size()<256){
					CStruct item = decode((Class<? extends CStruct>) type.getComponentType(),arrayBytes);
					list.add(item);
					arrayBytes = item.unusedBytes;
				}
				Object array = Array.newInstance(type.getComponentType(), list.size());
				for(int i=0;i<list.size();i++){
					Array.set(array, i, list.get(i));
				}
				field.set(instance, array);
			}else if(type==CStruct.class || type.getSuperclass()==CStruct.class){
				byte[] arrayBytes = bts;
				CStruct item = decode((Class<? extends CStruct>) type,arrayBytes);
				field.set(instance, item);
			}else{
				throw new RuntimeException(cls.getSimpleName()+"字段"+field.getName()+"类型未知");
			}
			instance.decodeByteMapping.put(name, bts);
			context.setVariable(name, field.get(instance));
		}
		byte[] useBytes = new byte[bited/8];
		System.arraycopy(bytes, 0 , useBytes, 0, bited/8);
		instance.decodeBytes = useBytes;
		byte[] unusedBytes = new byte[bytes.length-(bited/8)];
		if(unusedBytes.length>0){
			System.arraycopy(bytes, useBytes.length , unusedBytes, 0, unusedBytes.length);
		}
		instance.unusedBytes = unusedBytes;
		return (T) instance;
	}
	public static String toHex(byte[] bytes) {
        StringBuilder buf = new StringBuilder(bytes.length * 2);
        for(byte b : bytes) { // 使用String的format方法进行转换
            buf.append(String.format("%02x", new Integer(b & 0xff)));
        }
        return buf.toString().toUpperCase();
    }
    public static byte[] toBytes(String str) {
        if(str == null || str.trim().equals("")) return new byte[0];
        byte[] bytes = new byte[str.length() / 2];
        for(int i = 0; i < str.length() / 2; i++) {
            String subStr = str.substring(i * 2, i * 2 + 2);
            bytes[i] = (byte) Integer.parseInt(subStr, 16);
        }
        return bytes;
    }
	
    
    
    /**
	 * @功能: BCD码转为10进制串(阿拉伯数据)
	 * @参数: BCD码
	 * @结果: 10进制串
	 */
	public static String bcd2Str(byte[] bytes) {
		StringBuffer temp = new StringBuffer(bytes.length * 2);
		for (int i = 0; i < bytes.length; i++) {
			temp.append((byte) ((bytes[i] & 0xf0) >>> 4));
			temp.append((byte) (bytes[i] & 0x0f));
		}
		return temp.toString().substring(0, 1).equalsIgnoreCase("0") ? temp
				.toString().substring(1) : temp.toString();
	}

	/**
	 * @功能: 10进制串转为BCD码
	 * @参数: 10进制串
	 * @结果: BCD码
	 */
	public static byte[] str2Bcd(String asc) {
		int len = asc.length();
		int mod = len % 2;
		if (mod != 0) {
			asc = "0" + asc;
			len = asc.length();
		}
		byte abt[] = new byte[len];
		if (len >= 2) {
			len = len / 2;
		}
		byte bbt[] = new byte[len];
		abt = asc.getBytes();
		int j, k;
		for (int p = 0; p < asc.length() / 2; p++) {
			if ((abt[2 * p] >= '0') && (abt[2 * p] <= '9')) {
				j = abt[2 * p] - '0';
			} else if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
				j = abt[2 * p] - 'a' + 0x0a;
			} else {
				j = abt[2 * p] - 'A' + 0x0a;
			}
			if ((abt[2 * p + 1] >= '0') && (abt[2 * p + 1] <= '9')) {
				k = abt[2 * p + 1] - '0';
			} else if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
				k = abt[2 * p + 1] - 'a' + 0x0a;
			} else {
				k = abt[2 * p + 1] - 'A' + 0x0a;
			}
			int a = (j << 4) + k;
			byte b = (byte) a;
			bbt[p] = b;
		}
		return bbt;
	}
    
    
    public static class TestMessage {
		public @size(1) byte begin = 0x7e;//起始符
		public @size(2) short type = 0;//消息ID
		public @bit(2) byte retain;//消息体属性>保留
		public @bit(1) byte subpkg;//消息体属性>是否分包,0表示不分包
		public @bit(3) byte encrypt;//消息体属性>加密方式,0表示不加密
		public @bit(10) short length;//消息体长度
		public @size(6) @flag("BCD") String number;//手机号码
		public @size(2) short serial;//消息流水号
		public @expr("#subpkg==1?2:0") short pkgs;//分包数
		public @expr("#subpkg==1?2:0") short index;//包序号
		public @expr("#length") byte[] data;//数据部分
		public @size(1) byte verify;//校验码
		public @size(1) byte end = 0x7e;//结束符
	}
    
    /**
     * 求校验和的算法
     * @param b 需要求校验和的字节数组
     * @return 校验和
     */
    public static byte sumCheck(byte[] b,int start, int len){
        int sum = 0;
        for(int i = start; i < len; i++){
            sum = sum + b[i];
        }
        return (byte) (sum & 0xff);
    }
    
  //字符串转换为ascii
    public static byte[] StrToAsc(String content){
      try {
		return content.getBytes("US-ASCII");
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
    }
   
    //ascii转换为string
    public static String AscToStr(byte[] bytes){
    	StringBuffer sbu = new StringBuffer();  
    	for(int i=0;i<bytes.length;i++){
    		sbu.append(Character.toString((char)bytes[i]));
    	}
        return sbu.toString(); 
    }

    public static void trace(Object...objects){
    	Utils.trace(objects);
    }
    
	public static void main(String...strings) throws Exception{
		/*String hex = "B82300208103000889030008030800088D0300088F0300089103000800000000";
		byte[] bytes = CStruct.toBytes(hex);
		AscToStr(bytes);
		Utils.trace(new String(bytes,"UTF-8"));*/
		String code = "abcdefgdcv";
		byte[] bytes = StrToAsc(code);
		String newCode = AscToStr(bytes);
		Utils.trace(newCode);
	}
	
}